iT邦幫忙

2024 iThome 鐵人賽

DAY 3
0
JavaScript

PM說: RD大大,這個功能要怎麼寫啊?系列 第 3

PM說: 要怎麼寫出公車路線圖(網頁地圖)?

  • 分享至 

  • xImage
  •  

taichung-map

前言

昨天我們學了 leaflet 產生地圖&圖標&畫線
今天我們來思考常見的公車地圖如何實作出來

拆解題目

公車路線圖有2種資料

  1. 站牌
  2. 路線

我們都知道2點連成一線,路線其實就是超級多的點連接起來~
因此我們只要有辦法拿到該地區公車的站牌JSON & 路線JSON
搭配昨天學的方法就可以完成我們的目標!

如何取資料(JSON)

查了一下發現
"交通部"有建立一個「運輸資料流通服務平臺」TDX(Transport Data eXchange)
tdx-1

實際開發上需要call他們的API(要註冊會員拿到金鑰)

因為我們是demo只要知道JSON即可
所以直接找他們的範例來看:


(筆者撰寫日期: 2024/08/31, 不確定日後會不會改版)
以下拿台中市的289公車為例

  1. 進入查詢公車路線的網站
    https://ticp.motc.gov.tw/motcTicket/tools?tab=query-tab&city=Taichung&queryType=routeName&keyword=

  2. 搜尋 289 >> 點Table的TXG289 (網頁的檢查記得開著)

  3. 會看到call了3隻API
    tdx-bus

  4. 第1隻有"Stops"站牌資訊; 第3隻有"Geometry"公車路線資訊
    tdx-bus-api

成果

仿造台中公車縮放比例小時只顯示數字

放大時顯示數字和站名

程式碼

注意:站牌資料(stopData)&路線資料(routeData) 這邊省略

<!DOCTYPE html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Bus Route Map</title>
   <link
     rel="stylesheet"
     href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css"
   />
   <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
   <style>
     body {
       background-color: black;
       padding: 0;
       margin: 0;
     }
     .wrapper {
       width: 99dvw;
       height: 99dvh;
       display: flex;
       justify-content: center;
       align-items: center;
     }
     #map {
       height: 100%;
       width: 100%;
     }
   </style>
 </head>
 <body>
   <div class="wrapper">
     <div id="map"></div>
   </div>

   <!-- 引入站牌資料(stopData)&路線資料(routeData) -->
   <script src="./stopData.js"></script>
   <script src="./routeData.js"></script>
   <script>
     const myMap = {
       map: null,
       markers: [],
       init() {
         this.map = L.map("map").setView(
           [24.153940200805668, 120.6743698120117],
           14
         );
         //圖層
         L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
           attribution: "© OpenStreetMap contributors",
         }).addTo(this.map);
       },
       generateBusStop() {
         // 公車站牌資料
         const busStops = stopData;
         // 產生marker並添加到地圖
         busStops.forEach((stop) => {
           const marker = L.marker([
             stop.StopPosition.PositionLat,
             stop.StopPosition.PositionLon,
           ]).addTo(this.map);
           marker
             .bindTooltip(stop.StopID, { permanent: true, direction: "top" })
             .openTooltip();
           this.markers.push({ marker, stop });
         });
       },
       generateBusRoute() {
         // 公車路線
         const busRoute = {
           type: "LineString",
           coordinates: parseLineString(routeData.Geometry),
         };
         L.geoJSON(busRoute, {
           style: function (feature) {
             return { color: "blue", weight: 5 };
           },
         }).addTo(this.map);
       },

       main() {
         // 產生路線
         this.generateBusRoute();
         // 產生站牌
         this.generateBusStop();

         // 更新標記的顯示內容
         const updateMarkers = () => {
           const zoomThreshold = 15;
           const currentZoom = this.map.getZoom();
           this.markers.forEach(({ marker, stop }, index) => {
             const labelText =
               currentZoom >= zoomThreshold
                 ? `${index + 1}_${stop.StopName.Zh_tw}`
                 : `${index + 1}`;
             marker.setTooltipContent(labelText);
           });
         };
         // 初始化更新marker內容
         updateMarkers();
         // 當地圖縮放變化時更新marker
         this.map.on("zoomend", updateMarkers);
       },
     };

     myMap.init();
     myMap.main();

     function parseLineString(lineString) {
       // 移除 "LINESTRING(" 和 ")" 並分割座標對
       const coordinatesString = lineString
         .replace("LINESTRING(", "")
         .replace(")", "");
       const coordinatesArray = coordinatesString.split(",");

       // 將每個座標分割為經度和緯度,並轉換為 [經度, 緯度] 格式
       const coordinates = coordinatesArray.map((coord) => {
         const [lng, lat] = coord.trim().split(" ").map(Number);
         return [lng, lat];
       });

       return coordinates;
     }
   </script>
 </body>
</html>

重點

  1. 更改提示框的文字方法
    marker.setTooltipContent(要改的文字);

  2. 綁定縮放的事件監聽
    this.map.on("zoomend", updateMarkers);


上一篇
PM說: 要怎麼寫一個免費的網頁地圖?
下一篇
PM 說: 要怎麼寫外送平台的移動地圖抓地址?
系列文
PM說: RD大大,這個功能要怎麼寫啊?20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言